package com.photoeditor.demo.model.image;

import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PointF;
import android.view.MotionEvent;

import com.android.util.OpenCVHelper;
import com.common.base.log.DLog;
import com.common.base.utils.DeviceUtils;
import com.common.base.utils.thread.ThreadPool;

import java.util.ArrayList;
import java.util.List;

/**
 * 通过边缘检测技术，校正涂抹区域
 * Created by winn on 17/12/28.
 */

public class EdgeAvoidanceHelper {

    private static final String TAG = EdgeAvoidanceHelper.class.getSimpleName();

    private int mStrokeWidth = DeviceUtils.dip2px(15);
    /** 距离边缘点一定范围内进行吸附 **/
    private int mAdsorbDistance = DeviceUtils.dip2px(10);
    /** 距离边缘点过近时，不进行校正 **/
    private int mNearbyEdge = DeviceUtils.dip2px(5);

    private List<Point> mEdgePoints;

    private boolean mEnableEdgeAdjust = true;

    public EdgeAvoidanceHelper() {
    }

    /**
     * 设置需要图片，注意此图片需要与真正显示在屏幕上的图大小一致，而非原图
     * @param bitmap
     */
    public void setBitampSyncCanny(final Bitmap bitmap) {
        cannyBitmap(bitmap);
    }

    public void setBitampAsyncCanny(final Bitmap bitmap) {
        mEdgePoints = null;
        try {
            OpenCVHelper.clearAllPoint();
        } catch (UnsatisfiedLinkError error) {
            return;
        }
        ThreadPool.runOnNonUIThread(new Runnable() {
            @Override
            public void run() {
                cannyBitmap(bitmap);
            }
        });
    }

    public void setStrokeWidth(int strokeWidth) {
        mStrokeWidth = strokeWidth;
    }

    public void setAdsorbDistance(int adsorbDistance) {
        mAdsorbDistance = adsorbDistance;
    }

    public void enableEdgeAdjust(boolean enable) {
        mEnableEdgeAdjust = enable;
    }

    private void cannyBitmap(Bitmap bitmap) {
        Point[][] points = null;
        long time = System.currentTimeMillis();
//        if (TestUser.isTestUser()) {
//            final String dir = FileUtil.getExternalCacheDir(ComponentContext.getContext(), "canny") + File.separator;
////            BitmapUtils.savePngBitmap(bitmap, dir + (System.currentTimeMillis()) + ".png", 100);
//            Bitmap cannyBitmap = bitmap.copy(bitmap.getConfig(), false);
//            points = OpenCVHelper.cannyImg(bitmap, cannyBitmap);
//            BitmapUtils.savePngBitmap(cannyBitmap, dir + (System.currentTimeMillis()) + ".png", 100);
//            ThreadPool.runUITask(new Runnable() {
//                @Override
//                public void run() {
//                    ToastUtil.showToast(ComponentContext.getContext(), "canny finish  dir:" + dir);
//                }
//            });
//        } else {
            points = OpenCVHelper.cannyImg(bitmap, null);
//        }
        if (points != null) {
            List<Point> allPoints = new ArrayList<>();
            for (Point[] matOfPoint : points) {
                if (matOfPoint != null) {
                    for (Point tmp : matOfPoint) {
                        allPoints.add(tmp);
                    }
                }
            }
            DLog.i(TAG, "canny edge point:" + allPoints.size() + "  time:" + (System.currentTimeMillis() - time));
            mEdgePoints = allPoints;
            try {
                OpenCVHelper.setAllPoint(mEdgePoints.toArray());
            } catch (UnsatisfiedLinkError error) {

            }
        } else {
            if (mEdgePoints != null) {
                mEdgePoints.clear();
            }
            try {
                OpenCVHelper.clearAllPoint();
            } catch (UnsatisfiedLinkError e) {

            }
        }
    }

    public void onResume() {
        if (mEdgePoints != null) {
            try {
                OpenCVHelper.setAllPoint(mEdgePoints.toArray());
            } catch (UnsatisfiedLinkError error) {

            }
        }
    }

    public void onDestroy() {
        try {
            OpenCVHelper.clearAllPoint();
        } catch (UnsatisfiedLinkError error) {

        }
    }

    /**
     * 返回校正的坐标
     * @param event
     * @return
     */
    public PointF onTouchEvent(MotionEvent event) {
        PointF point = new PointF(event.getX(), event.getY());
        if (mEdgePoints == null || mEdgePoints.size() == 0 || !mEnableEdgeAdjust) {
            return point;
        }
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            edgeAdjust(point);
        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
            edgeAdjust(point);
        } else {
//            edgeAdjust(point);
        }
        return point;
    }

    private void edgeAdjust(PointF touch) {
        Point point;
        try {
            point = OpenCVHelper.edgeAdjust((int)touch.x, (int)touch.y, mStrokeWidth, mAdsorbDistance, mNearbyEdge);
        } catch (UnsatisfiedLinkError error) {
            return;
        }
        if (point != null) {
            touch.set(point.x, point.y);
        }
        /*float adjustX = 0;
        float adjustY = 0;
        // 找出触摸点附近的边缘点
        Point[] maybeInList = OpenCVHelper.findNeighbor((int) touch.x, (int) touch.y, mStrokeWidth + mAdsorbDistance);
        if (maybeInList == null) {
            return ;
        }
        for (Point point : maybeInList) {
            float distance = (float) Math.sqrt((touch.x - point.x) * (touch.x - point.x)
                    + (touch.y - point.y) * (touch.y - point.y));
            float offsetX = 0, offsetY = 0;

            // 在一定范围内进行边缘纠正，触摸点与边缘点过近时，不纠正
            if (distance < mStrokeWidth / 2 && distance > mNearbyEdge) {
                if (point.y == touch.y) { // 水平线段
                    if (touch.x > point.x) {
                        offsetX = mStrokeWidth / 2 - distance;
                    } else {
                        offsetX = -(mStrokeWidth / 2 - distance);
                    }
                } else if (point.x == touch.x) { // 垂直线段
                    if (touch.y > point.y) {
                        offsetY = mStrokeWidth / 2 - distance;
                    } else {
                        offsetY = -(mStrokeWidth / 2 - distance);
                    }
                } else {
                    // 算出x和y方向上的分量
                    float k = (float) ((touch.y - point.y) / (touch.x - point.x));
                    offsetY = (float) ((mStrokeWidth / 2 - distance) * Math.sin(Math.atan(k)));
                    offsetX = (float) ((mStrokeWidth / 2 - distance) * Math.cos(Math.atan(k)));
                    if (touch.x < point.x) {
                        offsetX = -offsetX;
                    }
                    if (touch.y < point.y) {
                        offsetY = -offsetY;
                    }
                }
            }
            // 找出最大的调整距离
            if ((adjustX == 0 || (adjustX > 0 && offsetX >= adjustX) || (adjustX < 0 && offsetX <= adjustX))
                    && (adjustY == 0 || (adjustY > 0 && offsetY >= adjustY) || (adjustY < 0 && offsetY <= adjustY))) {
                adjustX = offsetX;
                adjustY = offsetY;
            }
        }
        if (adjustX != 0 || adjustY != 0) {
            DLog.i(TAG, "adjust  x:" + adjustX + "  y:" + adjustY);
        }
        // x或y两分量上，如果有一个分量没有调整过位置，则计算该分量上是否需要吸附
        float adsorbX = 0, adsorbY = 0;
        if (adjustX == 0 || adjustY == 0) {
            for (Point point : maybeInList) {
                float distance = (float) Math.sqrt((touch.x - point.x) * (touch.x - point.x)
                        + (touch.y - point.y) * (touch.y - point.y));
                float offsetX = 0, offsetY = 0;
                // 在一定范围内产生吸附效果
                if (distance < mStrokeWidth / 2 + mAdsorbDistance) {
                    if (point.y == touch.y) { // 水平线段
                        if (touch.x > point.x) {
                            offsetX = -(distance - mStrokeWidth / 2);
                        } else {
                            offsetX = distance - mStrokeWidth / 2;
                        }
                    } else if (point.x == touch.x) { // 垂直线段
                        if (touch.y > point.y) {
                            offsetY = -(distance - mStrokeWidth / 2);
                        } else {
                            offsetY = distance - mStrokeWidth / 2;
                        }
                    } else {
                        // 算出x和y方向上的分量
                        float k = (float) ((touch.y - point.y) / (touch.x - point.x));
                        offsetY = (float) ((distance - mStrokeWidth / 2) * Math.sin(Math.atan(k)));
                        offsetX = (float) ((distance - mStrokeWidth / 2) * Math.cos(Math.atan(k)));
                        if (touch.x > point.x) {
                            offsetX = -offsetX;
                        }
                        if (touch.y > point.y) {
                            offsetY = -offsetY;
                        }
                    }
                }
                // 找出最小的吸附距离
                if (adjustX == 0 && (Math.abs(adsorbX) > Math.abs(offsetX) || adsorbX == 0)) {
                    adsorbX = offsetX;
                } else if (adjustY == 0 && (Math.abs(adsorbY) > Math.abs(offsetY) || adsorbY == 0)) {
                    adsorbY = offsetY;
                }
            }
            if (adsorbX != 0) {
                adjustX = adsorbX;
            }
            if (adsorbY != 0) {
                adjustY = adsorbY;
            }
            if (adjustX != 0 || adjustY != 0) {
                DLog.i(TAG, "adsorb  x:" + adjustX + "  y:" + adjustY);
            }
        }
        touch.set(touch.x + adjustX, touch.y + adjustY);*/
    }
}
